package com.uziot.activiti6.controller;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.uziot.activiti6.common.dto.ModelDTO;
import com.uziot.activiti6.common.util.Result;
import com.uziot.activiti6.controller.vo.DeploymentResponse;
import com.uziot.activiti6.service.ProcessDefinitionService;
import com.uziot.activiti6.service.ModelServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @author shidt
 * @version V1.0
 * @className ModelerController
 * @date 2021-02-26 10:38:41
 * @description
 */

@Slf4j
@Api(tags = "流程-模型设计接口")
@Controller
@RequestMapping(value = "/models")
public class ModelerController {

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private ProcessDefinitionService processDefinitionService;
    @Autowired
    private ModelServiceImpl modelService;

    @ApiOperation(value = "创建一个新的流程图")
    @GetMapping(value = "/newModel")
    public String createFlow() {
        //初始化一个空模型
        Model model = repositoryService.newModel();
        //设置一些默认信息
        String name = "new-process";
        String description = "";
        int revision = 1;
        String key = "process";

        ObjectNode modelNode = objectMapper.createObjectNode();
        modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
        modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
        modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision);

        model.setName(name);
        model.setKey(key);
        model.setMetaInfo(modelNode.toString());

        repositoryService.saveModel(model);
        String id = model.getId();

        //完善ModelEditorSource
        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("id", "canvas");
        editorNode.put("resourceId", "canvas");
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put("namespace",
                "http://b3mn.org/stencilset/bpmn2.0#");
        editorNode.put("stencilset", stencilSetNode);

        repositoryService.addModelEditorSource(id, editorNode.toString().getBytes(StandardCharsets.UTF_8));
        return "redirect:/editor?modelId=" + id;
    }

    @ApiOperation(value = "创建一个新的流程图,由提交数据创建")
    @PostMapping(value = "/newFlowModel")
    @ResponseBody
    public Result createNewFlowModel(@RequestBody ModelDTO modelDTO) {
        String id = modelService.create(modelDTO);
        return Result.ok().put("modelId", id);
    }

    @ApiOperation(value = "流程图列表")
    @ResponseBody
    @GetMapping(value = "/flowList")
    public List<Model> listFlow() {
        List<Model> flowList = repositoryService.createModelQuery().list();
        return flowList;
    }


    @ApiOperation(value = "已部署的流程列表")
    @ResponseBody
    @GetMapping(value = "/deployList")
    public List<DeploymentResponse> deployList() {
        List<Deployment> deployments = repositoryService.createDeploymentQuery().list();
        List<DeploymentResponse> deployList = new ArrayList<>();
        for (Deployment deployment : deployments) {
            DeploymentResponse deploymentResponse = new DeploymentResponse(deployment);
            // 查询实例KEY和实例NAME
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                    .deploymentId(deployment.getId()).singleResult();
            String modelKey = processDefinition.getKey();
            String name = processDefinition.getName();
            deploymentResponse.setInstanceKey(modelKey);
            deploymentResponse.setName(name);
            deployList.add(deploymentResponse);
        }
        return deployList;
    }


    @ApiOperation(value = "根据ID删除流程")
    @ResponseBody
    @GetMapping(value = "/flowDelete")
    public void flowDelete(@RequestParam(name = "id") String id) {
        System.out.println("id = " + id);
        repositoryService.deleteModel(id);
    }

    @ApiOperation(value = "删除所有流程")
    @ResponseBody
    @GetMapping(value = "/flowDeleteAll")
    public Result flowDeleteAll() {
        List<Model> list = repositoryService.createModelQuery().list();
        int i = 0;
        for (Model model : list) {
            repositoryService.deleteModel(model.getId());
            i++;
        }
        return Result.ok(String.valueOf(i));
    }


    @ApiOperation(value = "根据流程ID部署流程")
    @ResponseBody
    @GetMapping(value = "/deploy")
    public Object deploy(@RequestParam(name = "id") String id) throws Exception {
        //获取模型
        Model modelData = repositoryService.getModel(id);
        byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
        if (null == bytes) {
            return "模型数据为空，请先设计流程并成功保存，再进行发布。";
        }
        JsonNode modelNode = new ObjectMapper().readTree(bytes);
        BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
        if (model.getProcesses().size() == 0) {
            return "数据模型不符合要求，请至少设计一条主线程流。";
        }
        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);

        //发布流程
        String processName = modelData.getName() + ".bpmn20.xml";
        Deployment deployment = repositoryService.createDeployment()
                .name(modelData.getName())
                .addString(processName, new String(bpmnBytes, StandardCharsets.UTF_8))
                .deploy();
        modelData.setDeploymentId(deployment.getId());
        repositoryService.saveModel(modelData);
        return "success";
    }

    @ApiOperation(value = "上传流程文件并部署流程")
    @ResponseBody
    @PostMapping(value = "/uploadDeploy")
    public Object uploadDeploy(@RequestPart MultipartFile file) throws Exception {
        processDefinitionService.uploadStreamAndDeployment(file);
        return "success";
    }


    @ApiOperation(value = "上传流程文件作为待编辑对象")
    @ResponseBody
    @PostMapping(value = "/uploadModel")
    public Object uploadModel(@RequestPart MultipartFile file) {
        return modelService.uploadModel(file);
    }

    @ApiOperation(value = "删除部署的流程")
    @ResponseBody
    @GetMapping(value = "/deleteDeploy")
    public void deleteDeploy(@RequestParam(name = "id") String id) {
        repositoryService.deleteDeployment(id);
    }


    @ApiOperation(value = "删除所有部署的流程")
    @ResponseBody
    @GetMapping(value = "/deleteAllDeploy")
    public Result deleteAllDeploy() {
        int i = processDefinitionService.deleteAllProcessDefinition();
        return Result.ok().put("delete", i);
    }


    @ApiOperation(value = "导出编辑的流程设计")
    @GetMapping(value = "/exportModel", produces = "application/json;charset=utf-8")
    @ResponseBody
    public void export(@RequestParam("modelId") String modelId, HttpServletResponse response) {
        try {
            Model modelData = repositoryService.getModel(modelId);
            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
            JsonNode editorNode = new ObjectMapper()
                    .readTree(repositoryService.getModelEditorSource(modelData.getId()));
            BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
            BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
            byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);

            ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);

            String filename = bpmnModel.getMainProcess().getId() + ".bpmn20.xml";
            response.setContentType("application/xml");
            response.setHeader("Content-Disposition", "attachment; filename=" + filename);
            // 这句必须放到setHeader下面，否则10K以上的xml无法导出，
            IOUtils.copy(in, response.getOutputStream());
            response.flushBuffer();
        } catch (Exception e) {
            log.info("导出失败：modelId=" + modelId, e);
        }
    }
}
